/**
 * 
 */
package com.duowan.cms.parser.timetask;

import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;

import com.duowan.cms.common.domain.Page;
import com.duowan.cms.common.exception.BaseCheckedException;
import com.duowan.cms.common.util.DateUtil;
import com.duowan.cms.common.util.StringUtil;
import com.duowan.cms.common.util.ThreadUtil;
import com.duowan.cms.core.rmi.client.article.Article4TagListRemoteService;
import com.duowan.cms.core.rmi.client.article.ArticleRemoteService;
import com.duowan.cms.core.rmi.client.channel.ChannelRemoteService;
import com.duowan.cms.core.rmi.client.tag.TagRemoteService;
import com.duowan.cms.core.rmi.client.template.AutoFlushTagRemoteService;
import com.duowan.cms.core.rmi.client.template.AutoFlushTemplateRemoteService;
import com.duowan.cms.core.rmi.client.template.TemplateRemoteService;
import com.duowan.cms.dto.article.ArticleInfo;
import com.duowan.cms.dto.channel.ChannelInfo;
import com.duowan.cms.dto.template.AutoFlushTagInfo;
import com.duowan.cms.dto.template.AutoFlushTemplateInfo;
import com.duowan.cms.dto.template.TemplateCategory;
import com.duowan.cms.dto.template.TemplateInfo;
import com.duowan.cms.dto.template.TemplateSearchCriteria;
import com.duowan.cms.parser.TemplateBatchParserService;
import com.duowan.cms.parser.TemplateParserService;

/**
 * 刷新定时任务:
 */
@Service("flushTimeTask")
public class FlushTimeTaskImpl implements FlushTimeTask {

    private static Logger logger = Logger.getLogger(FlushTimeTaskImpl.class);

    @Autowired
    private ArticleRemoteService articleRemoteService;
    @Autowired
    private TagRemoteService tagRemoteService;
    @Autowired
    private ChannelRemoteService channelRemoteService;
    @Autowired
    private TemplateRemoteService templateRemoteService;
    @Autowired
    private Article4TagListRemoteService article4TagListRemoteService;
    @Autowired
    private TemplateParserService templateParserService;
    @Autowired
    private AutoFlushTemplateRemoteService autoFlushTemplateRemoteService;
    @Autowired
    private AutoFlushTagRemoteService autoFlushTagRemoteService;
    @Autowired
    private TemplateBatchParserService templateBatchParserService;

    // @Scheduled(fixedDelay = 300000)
    @Scheduled(cron = "0 0/15 * * * ?")
    @Override
    public void flushTemplatesAndTagsByEffectTag() {
        ThreadUtil.getThreadPool().execute(new Runnable() {
            @Override
            public void run() {
                final int times = 16; // 15分钟以内变化的
                long startTime = System.currentTimeMillis();
                logger.info("开始【全普通频道刷新】任务.");
                List<ChannelInfo> channelInfoList = channelRemoteService.getAllChannel();
                for (final ChannelInfo channelInfo : channelInfoList) {
                    if (channelInfo.isImportant()) // 如果是重点的，则忽略
                        continue;
                    ThreadUtil.getThreadPool().execute(new Runnable() {
                        @Override
                        public void run() {
                            flushOnTimes(channelInfo, times);
                        }
                    });
                }
                long endTime = System.currentTimeMillis();
                logger.info("结束【全普通频道刷新】任务,共耗时" + (endTime - startTime) + "毫秒");
            
            }
        });
    }

    @Scheduled(cron = "0 0/12 * * * ?")
    @Override
    public void flushImportantTemplatesAndTagsByEffectTag() {
        ThreadUtil.getThreadPool().execute(new Runnable() {
            @Override
            public void run() {
                final int times = 13; // 12分钟以内变化的
                long startTime = System.currentTimeMillis();
                logger.info("开始【全重点频道刷新】任务.");
                List<ChannelInfo> channelInfoList = channelRemoteService.getAllChannel();
                for (final ChannelInfo channelInfo : channelInfoList) {
                    if (!channelInfo.isImportant()) // 如果是普通的，则忽略
                        continue;
                    ThreadUtil.getThreadPool().execute(new Runnable() {
                        @Override
                        public void run() {
                            flushOnTimes(channelInfo, times);
                        }
                    });

                }
                long endTime = System.currentTimeMillis();
                logger.info("结束【全重点频道刷新】任务,共耗时" + (endTime - startTime) + "毫秒");
            
            }
        });
    }

    @Scheduled(cron = "0 0/8 * * * ?")
    @Override
    public void flushWWWTemplatesAndTagsByEffectTag() {
        ThreadUtil.getThreadPool().execute(new Runnable() {
            @Override
            public void run() {

                final int times = 9;
                final String channelId = "www"; // 固定为首页频道
                long startTime = System.currentTimeMillis();
                logger.info("开始【首页频道刷新】任务.");
                ThreadUtil.getThreadPool().execute(new Runnable() {
                    @Override
                    public void run() {
                        flushOnTimes(channelRemoteService.getById(channelId), times);
                    }
                });
                long endTime = System.currentTimeMillis();
                logger.info("结束【首页频道刷新】任务,共耗时" + (endTime - startTime) + "毫秒");
            
            }
        });
    }

    /**
     *  刷新频率：0 0/15 * * * ? 
     * 处理预发布的文章
     */
    @Scheduled(cron = "0 0/15 * * * ?")
    @Override
    public void publishPrepubArticle() {
        ThreadUtil.getThreadPool().execute(new Runnable() {
            @Override
            public void run() {
                long startTime = System.currentTimeMillis();
                logger.info("开始【预定发表文章】任务");
                List<ArticleInfo> prepublishArticleList = articleRemoteService.getPrepublishArticle();
                logger.info("处理预定发表的文章总数:" + prepublishArticleList.size());
                for (ArticleInfo articleInfo : prepublishArticleList) {
                    try {
                        //注意修改更新时间!文章列表才会采集到，在列表页中刷新
                        articleInfo.setLastUpdateTime(new Date());
                        articleRemoteService.publish(articleInfo);
                    } catch (BaseCheckedException e) {
                        e.printStackTrace();
                    }
                }
                long endTime = System.currentTimeMillis();
                logger.info("结束【预定发表文章】任务,共耗时" + (endTime - startTime) + "毫秒");
            }
        });
    }
    /**
     *  刷新频率：0 0/15 * * * ? 
     * 处理预发布的空链接
     */
    @Scheduled(cron = "0 0/15 * * * ?")
    @Override
    public void publishPreEmptyLink() {
        ThreadUtil.getThreadPool().execute(new Runnable() {
            @Override
            public void run() {
                long startTime = System.currentTimeMillis();
                logger.info("开始【预定空链接】任务");
                try {
                    articleRemoteService.handlePrepublishEmptyLink();
                } catch (BaseCheckedException e) {
                    e.printStackTrace();
                }
                long endTime = System.currentTimeMillis();
                logger.info("结束【预定空链接】任务,共耗时" + (endTime - startTime) + "毫秒");
            }
        });
    }

    /**
     *  刷新频率：0 0/15 * * * ? 
     *  刷新指定的模板
     */
    @Scheduled(cron = "0 0/15 * * * ?")
    @Override
    public void flushSpecifiedTermplate() {
        ThreadUtil.getThreadPool().execute(new Runnable() {
            @Override
            public void run() {

                long startTime = System.currentTimeMillis();
                logger.info("开始【刷新指定模板】任务");
                List<AutoFlushTemplateInfo> flushTemplateInfos = autoFlushTemplateRemoteService.getAllAutoFlushTemplate();
                
                logger.info("开始【刷新指定模板】任务,需要刷新的模板数："+flushTemplateInfos.size());
                
                for (AutoFlushTemplateInfo autoFlushTemplateInfo : flushTemplateInfos) {
                    Date date = autoFlushTemplateInfo.getLastFlushTime(); // 最后刷新时间
                    int interval = autoFlushTemplateInfo.getInterval(); // 间隔时间
                    if (date != null) {
                        Date date2 = new Date(date.getTime() + interval * 1000);
                        if (DateUtil.compareDateTimeIgnoreMillisecond(date2, new Date()) > 0) {
                            continue;
                        }
                    }
                    // 需要自动刷新的模板
                    TemplateInfo templateInfo = templateRemoteService.getByChannelIdAndTemplateId(autoFlushTemplateInfo.getChannelInfo().getId(), autoFlushTemplateInfo.getTemplateId());
                    if (templateInfo == null)
                        continue;
                    try {
                        if (TemplateCategory.TAG.equals(templateInfo.getTemplateCategory()) || TemplateCategory.TAG_VIEW.equals(templateInfo.getTemplateCategory())) {  //如果是“标签/标签图”模板，需要刷新全部标签
                            templateBatchParserService.flushAllTagInChannel(templateInfo.getChannelId());
                        } else {
                            templateParserService.parseTemplateAndGenerateFile(templateInfo.getChannelInfo(), templateInfo);
                        }
                        logger.info("开始【刷新指定模板】任务,==============1");
                        autoFlushTemplateInfo.setLastFlushTime(new Date());
                        logger.info("开始【刷新指定模板】任务,==============2");
                        autoFlushTemplateRemoteService.save(autoFlushTemplateInfo);
                        logger.info("开始【刷新指定模板】任务,==============3");
                    } catch (Throwable e) {
                        e.printStackTrace();
                        logger.info("开始【刷新指定模板】任务,==============4");
                        logger.error("模板[channelId=" + templateInfo.getChannelInfo().getId() + ", id=" + templateInfo.getId() + "]解析有误，原因：" + e.getMessage());
                    }
                }
                logger.info("开始【刷新指定模板】任务,==============5");
                long endTime = System.currentTimeMillis();
                logger.info("结束【刷新指定模板】任务,共耗时" + (endTime - startTime) + "毫秒");
            }
        });
    }

    /**
     * 刷新某个频道指定时间内（以分钟为最小单位）内有影响的模板
     * @param channelInfo
     * @param times
     */
    private void flushOnTimes(ChannelInfo channelInfo, int times) {
        List<String> effectTagList = article4TagListRemoteService.getRecentEffectTagsInArticle(channelInfo.getId(), times);
        if (effectTagList == null || effectTagList.isEmpty())
            return;
        this.flushTagsByTagsList(channelInfo, effectTagList);
    }

    @Override
    public void flushCooperateTemplate() {
        // TODO 参照旧代码 Post_bs.flushCooperateTemplate()
    }

    @Override
    public void flushTagByMinute() {
        // TODO 参照旧代码 Post_bs.flushTagByMinute()
    }

    @Override
    public void flushVote() {
        // TODO 参照旧代码 Vote.flushVote()

    }

    // @Scheduled(fixedDelay = 60000)
    @Scheduled(cron = "0 0/1 * * * ?")
    @Override
    public void fetchTagsNeedFlush() {
        final int times = 2; // 2分钟以内变化的
        long startTime = System.currentTimeMillis();
        logger.info("开始【采集最近更新过的tag】任务。");
        List<ChannelInfo> channelInfoList = channelRemoteService.getAllChannel();
        for (final ChannelInfo channelInfo : channelInfoList) {
            List<String> effectTagList = article4TagListRemoteService.getRecentEffectTagsInArticle(channelInfo.getId(), times);
            if (effectTagList == null || effectTagList.isEmpty())
                continue;
            for (String tagName : effectTagList) {
                if (StringUtil.isEmpty(tagName))
                    continue;
                AutoFlushTagInfo autoFlushTagInfo = new AutoFlushTagInfo(channelInfo.getId(), tagName);
                if (!autoFlushTagRemoteService.isExistUnFlushTag(channelInfo.getId(), tagName)) {
                    autoFlushTagRemoteService.save(autoFlushTagInfo);
                    logger.info("在专区" + channelInfo.getId() + "内采集到需要刷新的tag：【" + tagName + "】。");
                } else {
                    logger.info("在专区" + channelInfo.getId() + "内的tag：【" + tagName + "】已经存在刷新队列，无需重复采集。");
                }
            }
        }
        long endTime = System.currentTimeMillis();
        logger.info("结束【采集最近更新过的tag】任务,共耗时" + (endTime - startTime) + "毫秒");
    }

    @Scheduled(fixedDelay = 60000)
    // @Scheduled(cron = "0 0/1 * * * ?")
    @Override
    public void flushTagsByFetched() {
        long startTime = System.currentTimeMillis();
        logger.info("开始【刷新auto_flush记录的tag】任务.");
        List<AutoFlushTagInfo> unFlushTagInfos = autoFlushTagRemoteService.listUnFlushTag();
        logger.info("需要刷新的tag的数量是：" + unFlushTagInfos.size());

        if (!unFlushTagInfos.isEmpty()) {
            // 把获取的tag整理成按专区分类保存到Map
            final Map<String, List<AutoFlushTagInfo>> autoTagInfoMap = new HashMap<String, List<AutoFlushTagInfo>>();
            final Map<String, Set<String>> tagMap = new HashMap<String, Set<String>>();
            for (AutoFlushTagInfo autoFlushTagInfo : unFlushTagInfos) {
                String channelId = autoFlushTagInfo.getChannelId();
                String tag = autoFlushTagInfo.getTagName().trim();
                if (tagMap.get(channelId) == null) {
                    Set<String> tagsSet = new HashSet<String>();
                    tagsSet.add(tag);
                    tagMap.put(channelId, tagsSet);
                } else {
                    tagMap.get(channelId).add(tag);
                }
                if (autoTagInfoMap.get(channelId) == null) {
                    List<AutoFlushTagInfo> autoFlushTagInfos = new ArrayList<AutoFlushTagInfo>();
                    autoFlushTagInfos.add(autoFlushTagInfo);
                    autoTagInfoMap.put(channelId, autoFlushTagInfos);
                } else {
                    autoTagInfoMap.get(channelId).add(autoFlushTagInfo);
                }
            }
            // 逐个遍历保存的map专区，刷新
            final Set<Map.Entry<String, Set<String>>> set = tagMap.entrySet();
            for (Iterator<Map.Entry<String, Set<String>>> it = set.iterator(); it.hasNext();) {
                Map.Entry<String, Set<String>> entry = (Map.Entry<String, Set<String>>) it.next();
                final String channelId = entry.getKey();
                final Set<String> tagsSet = entry.getValue();
                // 刷新操作需要一些时间，在这段时间内，为了允许同名的tag插入，把flushtime预先设置开始刷新时间 ,表示已经开始刷新
                final List<AutoFlushTagInfo> autoFlushTagInfos = autoTagInfoMap.get(channelId);
                // flushTagsByTagsList(channelRemoteService.getById(channelId),
                // new ArrayList(tagsSet));
                // autoFlushTagRemoteService.batcFinishedFlushTask(autoFlushTagInfos);
                ThreadUtil.getThreadPool().execute(new Runnable() {
                    // 使用独立线程处理，为了防止定时任务阻塞
                    public void run() {
                        autoFlushTagRemoteService.batcBeginFlushTask(autoFlushTagInfos); // 设置刷新的开始时间
                        flushTagsByTagsList(channelRemoteService.getById(channelId), new ArrayList(tagsSet));
                        autoFlushTagRemoteService.batcFinishedFlushTask(autoFlushTagInfos); // 设置刷新的结束时间
                    }
                });
            }
        }
        long endTime = System.currentTimeMillis();
        logger.info("结束【刷新auto_flush记录的tag】任务,共耗时" + (endTime - startTime) + "毫秒");
    }

    // 刷新跨专区调用的模板（根据模板的相关的tag中是否含有":"来判断是否跨专区调用的模板）
    @Scheduled(cron = "0 0/15 * * * ?")
    @Override
    public void flushTagsCrossChannel() {
        long startTime = System.currentTimeMillis();
        logger.info("开始【刷新跨专区调用的模板】任务.");
        int pageSize = 100;
        int pageNo = 1;
        TemplateSearchCriteria criteria = new TemplateSearchCriteria();
        criteria.setPageNo(pageSize);
        criteria.setPageNo(pageNo);
        criteria.addRelateTags(":");
        while (true) {
            final Page<TemplateInfo> page = templateRemoteService.pageSearch(criteria);
            if (page == null || page.isEmpty()) {
                break;
            } else {
                ThreadUtil.getThreadPool().execute(new Runnable() {
                    public void run() {
                        List<TemplateInfo> templateInfos = page.getResult();
                        for (int i = 0; i < templateInfos.size(); i++) {
                            try {
                                TemplateInfo templateInfo = templateInfos.get(i);
                                if (templateInfo == null)
                                    continue;
                                templateParserService.parseTemplateAndGenerateFile(channelRemoteService.getById(templateInfo.getChannelId()), templateInfo);
                            } catch (BaseCheckedException e) {
                                logger.error("解析模板[" + templateInfos.get(i).getId() + "]出错，原因：" + e.getMessage());
                            }
                        }
                    }
                });
            }
            criteria.setPageNo(pageNo++);
        }
        long endTime = System.currentTimeMillis();
        logger.info("结束【刷新跨专区调用的模板】任务,共耗时" + (endTime - startTime) + "毫秒");
    }

    // 把List中的tag赋值到String中，每个之间使用“,”隔开
    private String tagListToStr(List<String> effectTagList) {
        StringBuffer tags = new StringBuffer();
        for (int i = 0; i < effectTagList.size(); i++) {
            if (i > 0)
                tags.append(",");
            tags.append(effectTagList.get(i));
        }
        return tags.toString();
    }

    /**
     * 使用独立的线程，刷新指定频道下的指定的tags(多个)对应的模板 （比较耗时，需优化）
     * @param channelInfo
     * @param effectTagList
     */
    private void flushTagsByTagsList(final ChannelInfo channelInfo, final List<String> effectTagList) {
        String tags = tagListToStr(effectTagList); // 每个之间使用“,”隔开
        logger.info("开始【" + channelInfo.getId() + "频道，(" + tags + ")标签刷新】任务。");
        long startTime = System.currentTimeMillis();
        logger.info("[" + channelInfo.getName() + "(" + channelInfo.getId() + ")" + "]频道需要刷新的标签有：" + tags);
        TemplateSearchCriteria searchCriteria = new TemplateSearchCriteria();
        searchCriteria.addRelateTags(tags);
        searchCriteria.setChannelId(channelInfo.getId());
        searchCriteria.setKeyword("data."); // 只有模板中包含了"data.XXXX"的方法的才需要刷新
        List<TemplateInfo> templateInfoList = templateRemoteService.listSearch(searchCriteria);
        // 刷新模板静态页面
        for (TemplateInfo templateInfo : templateInfoList) {
            try {
                templateParserService.parseTemplateAndGenerateFile(channelInfo, templateInfo);
            } catch (BaseCheckedException e) {
                logger.error("刷新模板[channelId=" + channelInfo.getId() + ", templateId=" + templateInfo.getId() + "]的定时任务出错，错误原因：", e);
                e.printStackTrace();
            }
        }
        logger.info("[" + channelInfo.getName() + "(" + channelInfo.getId() + ")" + "]频道刷新标签：" + tags+"对应的模板静态页面成功。");
        // 刷新tag静态页面
        for (String tag : effectTagList) {
            try {
                templateParserService.parseTagAndGenerateFile(channelInfo, tagRemoteService.getByChannelIdAndName(channelInfo.getId(), tag));
            } catch (BaseCheckedException e) {
                logger.error("刷新标签[channelId=" + channelInfo.getId() + ", tag=" + tag + "]的定时任务出错，错误原因：", e);
                e.printStackTrace();
            }
        }
        long endTime = System.currentTimeMillis();
        logger.info("结束【" + channelInfo.getId() + "频道，(" + tags + ")标签刷新】任务,共耗时" + (endTime - startTime) + "毫秒");
    }

}
